package bcm.bridge.center.printer;

import bcm.security.BridgeSecurity;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;

public class InvertBridgeManager extends HttpServlet implements Runnable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -7873122313270206938L;
	private static InvertBridgeManager instance;
	private static int defaultListenPort=8888;

	private BridgeSecurity bridgeSecurity;
	
	Log logger=LogFactory.getLog(getClass());
//	public static Map<String,CommBridgeThread> commBridgeThreads=new HashMap<>();
	public static Semaphore commBridgeThreadLocker=new Semaphore(20);
	
	public static Map<String,PrintBridgeThread> printBridgeThreads=new HashMap<>();
	public static Semaphore printBridgeThreadLocker=new Semaphore(20);
	
	public static Map<String,BridgeState> onlineCommBridgeStateMap=new HashMap<>();
	
	public static Map<String,BridgeState> onlinePrintBridgeStateMap=new HashMap<>();
	
	private int listenPort=defaultListenPort;
	private Thread thread;
	private boolean stop=false;
	
	private long interval=5000;
	
	public static InvertBridgeManager getInstance(){
		return instance;
	}
	
	public InvertBridgeManager(){
		if(instance==null) instance=this;
	}
	
	public InvertBridgeManager(int listenPort){
		this.listenPort=listenPort;
	}

//	public CommBridgeThread getCommBridgeThread(String bridgeId){
//		return commBridgeThreads.get(bridgeId);
//	}
	
	public PrintBridgeThread getPrintBridgeThread(String bridgeId){
		return printBridgeThreads.get(bridgeId);
	}
		
//	public Set<String> listCommBridges(){
//		return commBridgeThreads.keySet();
//	}
	
	public Set<String> listPrintBridges(){
		return printBridgeThreads.keySet();
	}
	
	public int getListenPort() {
		return listenPort;
	}

	public void setListenPort(int listenPort) {
		this.listenPort = listenPort;
	}
	
	public void run(){
		try {
			ServerSocket serverSocket=new ServerSocket(listenPort);
			logger.info("InvertBridge listen:".concat(serverSocket.toString()));
			serverSocket.setSoTimeout((int) interval);
			Socket clientSocket=null;
			while(!stop){
				Thread.sleep(interval);
				try{
					clientSocket=serverSocket.accept();
					if(clientSocket==null) continue;
				}catch(SocketTimeoutException e){
					continue;
				}			
				try{
					clientSocket.setSoTimeout(10000);
					TSocket transport=new BridgeSocket(clientSocket);
					TProtocol protocol=new TBinaryProtocol(transport);
					if(protocol.readByte()!=1) throw new Exception("protocol error1 ".concat(clientSocket.toString()));
					String _role=new String(Base64.decodeBase64(protocol.readString()),"utf8");
					if(!_role.startsWith("R:")||_role.length()<3) throw new Exception("protocol error2 ".concat(clientSocket.toString()));				
					String bridgeRole=_role.substring(2);
					if(protocol.readByte()!=4) throw new Exception("protocol error3 ".concat(clientSocket.toString()));
//					if(bridgeRole.equals("commBridge")){
//						CommBridgeThread thread=new CommBridgeThread(protocol,transport);
//						thread.start();
//					}else
						if(bridgeRole.equals("printBridge")){
						PrintBridgeThread thread=new PrintBridgeThread(protocol,transport,bridgeSecurity);
						thread.start();
					}else{
						throw new Exception("bridge role error:".concat(bridgeRole).concat(",").concat(clientSocket.toString()));
					}					
				}catch(Exception e){
					try{
						clientSocket.close();
					}catch(Exception e1){}
					if(e.getMessage()!=null){
						logger.error(e.getMessage());
					}else e.printStackTrace();
					continue;
				}
			}
			serverSocket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		logger.info("InvertBridge stopped");
	}
	
	public static void main(String[] args) throws ServletException{
		new InvertBridgeManager().init();
	}

	
	@Override
	public void init() throws ServletException{
		super.init();

		String security = StringUtils.trimToEmpty(getInitParameter("security"));
		WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
		bridgeSecurity = (BridgeSecurity) context.getBean(security);

		String stemp=StringUtils.trimToEmpty(getInitParameter("listenPort"));
		if(!stemp.isEmpty()) setListenPort(Integer.parseInt(stemp));
		thread=new Thread(this);
		thread.start();
	}

	@Override
	public void destroy(){
		stop=true;
		super.destroy();
	}
	
	
	
}
